#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <math.h>
#include <mpi.h>

#define ORIGEM 0
//#define max_palavra 400000

/*
	tempo: armazena o tempo gasto por aquele processo
	nPalinW: armazena o número de palindromos no texto wikipedia.txt
    	nPalinS: armazena o número de palindromos no texto shakespe.txt
   	n_primos: armazena o número de palindromos que também são primos
    	n_frase_pal: armazena o número de palindromos encontrado em frases
*/

int separa_arq(char nome[], int num_procs);
int separa_arq_frase(char nome[], int num_procs);
int tamanho_max_word(char nome[]);
int tamanho_max_phrase(char nome[]);
int palindromo(char s[]);
void crivo(int *primos,float max);
int isprimo(int *primos,char s[],int max);

int n_processos;
int nProcW = 1, nProcS = 1, nProcF = 1;

int main(int argc, char **argv)
{

    /*
	max_palavra: armazena o tamanho da maior palavra encontrada no texto
	max_frase: armazena o tamanho da frase mais longa do texto
	primos: vetor utilizado pelo Crivo de Erastóteles para o cálculo de números primos
	rc: armazena se mpi retornou com sucessos
	n_processos: armazena quantos processos serão rodados
	recebido: vetor que armazena MPI_SUCCESS para cada processo que estiver rodando
	palavra: armazena a palavra/frase para a verificação de palíndromos
	fp: arquivo texto que será lido
	inicio/fim: marcadores de tempo
    */
    
    double tempo = 0;
    int rc, id_process, i = 0;
    int max_palavra, max_frase, *primos = NULL;
    
    int *nPalinW = NULL, *nPalinS = NULL, *nFrases = NULL, *nPrimo = NULL, *fPalinW = NULL, *fPalinS = NULL, *fFrases = NULL, *fPrimo = NULL;
    int total_p_wiki = 0, total_p_shake = 0, total_frases = 0, total_primo = 0, fake_id;
    char *palavra = NULL ,arq[10], *frase = NULL;
    FILE *fp = NULL;
    time_t inicio, fim;

    rc = MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &n_processos);
    MPI_Comm_rank(MPI_COMM_WORLD, &id_process);

    nPalinW = calloc(nProcW ,sizeof(int));
    nPalinS = calloc(nProcS, sizeof(int)); 
    nFrases = calloc(nProcF, sizeof(int));
    nPrimo = calloc(nProcW, sizeof(int));
    fPalinW = calloc(nProcW, sizeof(int));
    fPalinS = calloc(nProcS, sizeof(int)); 
    fFrases = calloc(nProcF, sizeof(int));
    fPrimo = calloc(nProcW, sizeof(int));

    
    /* Pré processa o arquivo shakespe.txt */
    max_palavra = tamanho_max_word("shakespe.txt")+1;
    //separa_arq("shakespe.txt", nProcS);
    palavra = malloc(sizeof(char)*max_palavra);
    
    /* Divide o arquivo em partes menores para ser calculado */
    max_frase = tamanho_max_phrase("shakespe.txt")+1;
    //separa_arq_frase("shakespe.txt", nProcF);
    frase = malloc(sizeof(char)*max_frase);

    

    /* Fim da declaração da nova variável MPI */

    if(rc == MPI_SUCCESS)			
    {
	time(&inicio);

	/* TEXTO GRANDE (wikipedia) */
        if(id_process == 1 )//> ORIGEM && id_process < nProcW + 1)
        {	  

            /* Pré processa o arquivo wikipedia.txt dividindo o arquivo em partes menores */
	    //max_palavra = tamanho_max_word("wikipedia.txt")+1;
	    primos = malloc(sizeof(int)*((max_palavra*90)+1));
	    //separa_arq("wikipedia.txt", nProcW);  

	    /* Calcula o crivo */
            crivo(primos,(max_palavra*90)+1);
           //palavra = malloc(sizeof(char)*max_palavra);

            //sprintf(arq,"%d",id_process-1);
	    //strcat(arq,"wikipedia");            
	    //strcat(arq,".txt");
	    //printf("%d] Tentando ler %s\n", id_process, arq);

            //fp = fopen(arq,"r");
	    fp = fopen("wikipedia.txt","r");
	
	    if(fp == NULL)
		exit(-666);

            while(fscanf(fp,"%s",palavra)!=EOF)
            {
                if(palindromo(palavra)==1)
                {
                    nPalinW[0]++;
                    nPrimo[0] += isprimo(primos,palavra,strlen(palavra));
                }
            }
	    free(primos);

	    /* Envia uma mensagem com o número de palindromos calculados e quantos deles são primos */
            //rc = MPI_Send(&msg, 1, t_msg, ORIGEM, 1, MPI_COMM_WORLD);
        }

        /*TEXTO MENOR (Shakespe) - verifica todas as palavras */
        if(id_process == 2 )//>= nProcW + 1 && id_process < nProcW + nProcS + 1)
        {
            //sprintf(arq,"%d",id_process - nProcW - 1);
	    //strcat(arq,"shakespe");
            //strcat(arq,".txt");

            //fp = fopen(arq,"r");
	    fp = fopen("shakespe.txt","r");
	    if(fp == NULL)
		exit(-666);

            while(fscanf(fp,"%s",palavra)!=EOF)
            {
                nPalinS[0] += palindromo(palavra);
            }

	    /* Envia uma mensagem com o número de palavras palindromos calculados */
	    //rc = MPI_Send(&msg, 1, t_msg, ORIGEM, 1, MPI_COMM_WORLD);
        }

	/*TEXTO MENOR (Shakespe) - verifica as frases  */
        if(id_process == 3)//>= nProcW + nProcS + 1 && id_process < nProcW + nProcS + nProcF + 1)
        {

	    

            //sprintf(arq,"%d",id_process - nProcW - nProcS - 1);
	    //strcat(arq,"shakespe");
            //strcat(arq,".txt");

            //fp = fopen(arq,"r");
	    fp = fopen("shakespe.txt","r");

	    if(fp == NULL)
		exit(-666);

            while(fscanf(fp,"%s",palavra)!=EOF)
            {
                nFrases[0] += palindromo(frase);
            }
            
            
	    /* Envia uma mensagem com o número de frases palindromos calculados */
	    //rc = MPI_Send(&msg, 1, t_msg, ORIGEM, 1, MPI_COMM_WORLD);
        }


	/**
	 * MPI_Gather(&a[processId], 1, MPI_INT, b, 1, MPI_INT, 0, MPI_COMM_WORLD);
	 * O processo especificado(root)  recebe informação de todos os processos em um grupo de comunicação
	 *
	 * Parâmetros:
	 * &a[processId]: buffer de envio que contém o valor a ser passado para o processo especificado
	 * 1: sendcount tamanho do buffer de envio 
	 * * MPI_INT: Tipo do dado a ser enviado
	 * b: buffer de destino
	 * 1: tamanho dos dados que cada unidade do buffer vai receber
	 * MPI_INT: Tipo de dado a ser recebido
	 * 0: id do processo especificado
	 * MPI_COMM_WORLD: Comunicador
	 */

	MPI_Gather(&nPalinW[id_process], 1 , MPI_INT, &fPalinW[id_process], 1, MPI_INT, ORIGEM, MPI_COMM_WORLD);
	MPI_Gather(&nPalinS[id_process], 1 , MPI_INT, &fPalinS[id_process], 1, MPI_INT, ORIGEM, MPI_COMM_WORLD);
	MPI_Gather(&nFrases[id_process], 1 , MPI_INT, &fFrases[id_process], 1, MPI_INT, ORIGEM, MPI_COMM_WORLD);
	MPI_Gather(&nPrimo[id_process], 1 , MPI_INT, &fPrimo[id_process], 1, MPI_INT, ORIGEM, MPI_COMM_WORLD);

	/* Processo principal: é responsável por verificar se todos os processos terminaram e junta as informações calculadas */
        if(id_process == ORIGEM)
        {
		for(i = 0; i < nProcW; i++)
		{
			total_p_wiki += fPalinW[i];
			total_primo += fPrimo[i]; 
		}		
		for(i = 0; i < nProcS; i++)
			total_p_shake += fPalinS[i];
		for(i = 0; i < nProcF; i++)
			total_frases += fFrases[i];
		time(&fim);
    		tempo = difftime(fim, inicio);

		printf("Resultados:\n -Tempo: %.2fs\n", tempo);
   		printf("Wikipedia:\n -Palindromos: %d\n -Primos: %d\n", total_p_wiki , total_primo);
    		printf("Shakespeare:\n -Palindromos: %d\n -Frases Palindromo: %d\n", total_p_shake, total_frases);	
        }		

	
    }
    printf("%d] Funciona!\n", id_process);
    free(nPalinW);
    free(nPalinS);
    free(nFrases);
    free(nPrimo);
    free(fPalinW);
    free(fPalinS);
    free(fFrases);
    free(fPrimo);
    free(frase);
   // free(primos);
    free(palavra);

    
    MPI_Finalize();

    

    return 0;
}

int isprimo(int *primos,char s[],int max)
{
    int i,k;
    k = 0;
    for(i = 0; i<max; i++)
    {
        k += (int)s[i];
    }
    return primos[k];
}


void crivo(int *primos,float max)
{
    int n;
    int i,j;
    n = (int) sqrt(max);
    int k = (int) max;
    for(i = 0; i<max; i++)
    {
        primos[i] = 1;
    }
    for(i = 2; i < n; i++)
    {
        if(primos[i] == 1)
        {
            for(j = i + 1; j < k; j++)
            {
                if((j%i)==0)
                    primos[j] = 0;
            }
        }
    }
}

int palindromo(char s[])
{

    int j,k;
    if(strlen(s)<3) return 0;

    for(j = 0,k = strlen(s)-1; j <= k; j++,k--)
    {
        if(s[j] != s[k])
            return 0;
    }
    return 1;
}

/*tamanho da maior palavra encontrada*/
int tamanho_max_word(char nome[])
{
    FILE *fp = NULL;
    int count = 0;
    int max = 0;
    char c;
    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        count++;
        if(isspace(c))
        {
            if(count > max) max = count;
            count = 0;
        }
    }
    fclose(fp);
    return max;
}

/*tamanho da maior palavra encontrada*/
int tamanho_max_phrase(char nome[])
{
    FILE *fp = NULL;
    int count = 0;
    int max = 0;
    char c;
    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        count++;
        if(c == '.' || c == '!' || c == '?')
        {
            if(count > max) max = count;
            count = 0;
        }
    }
    fclose(fp);
    return max;
}

int separa_arq(char nome[], int num_procs)
{
    FILE *fp = NULL;
    FILE *ftk = NULL;

    int count = 0;
    int div = 0;
    int min_procs = 0;

    char c;
    char arq[10];

    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        if(c == '.')count++;
    }
    div = count/num_procs;
    div++;
    rewind(fp);

    count = 0;

    sprintf(arq,"%d",min_procs);
    strcat(arq, nome);
    strcat(arq,".txt");
    ftk = fopen(arq,"w");


    while(!feof(fp))
    {
        c = fgetc(fp);
        if(isalpha(c) || isspace(c))
        {
            fputc(toupper(c),ftk);
        }
        else
        {
            if(c == '.')
            {
                count++;
                fputc(' ',ftk);
                fputc(toupper(c),ftk);
            }

        }


        if(count == div)
        {
            count = 0;
            fclose(ftk);
            min_procs++;
            sprintf(arq,"%d",min_procs);
	    strcat(arq, nome);
            strcat(arq,".txt");
            ftk = fopen(arq,"w");
        }
    }
    fclose(ftk);
    fclose(fp);
    return 0;
}

int separa_arq_frase(char nome[], int num_procs)
{
    FILE *fp = NULL;
    FILE *ftk = NULL;

    int count = 0;
    int div = 0;
    int min_procs = 0;


    char c;
    char arq[10];

    fp = fopen(nome,"r");

    while(!feof(fp))
    {
        c = fgetc(fp);
        if(c == '.' || c =='!' || c == '?')count++;
    }
    div = count/num_procs;
    div++;
    rewind(fp);

    count = 0;

    sprintf(arq,"%d",min_procs);
    strcat(arq,".txt");
    ftk = fopen(arq,"w");

    while(!feof(fp))
    {
        c = fgetc(fp);
        if(isalpha(c) || isspace(c))
        {
            if(c != ' ')
                fputc(toupper(c),ftk);
        }
        else
        {
            if(ispunct(c))
            {
                if(c == '.' || c =='!' || c == '?')
                {
                    count++;
                    fputc(' ',ftk);
                }


            }
        }


        if(count == div)
        {
            count = 0;
            fclose(ftk);
            min_procs++;
            sprintf(arq,"%d",min_procs);
            strcat(arq,".txt");
            ftk = fopen(arq,"w");
        }
    }
    fclose(ftk);
    fclose(fp);
    return 0;
}

